package org.loujiarong.yugioh.gameplay.duel;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;

import org.loujiarong.yugioh.gameplay.card.Card;
import org.loujiarong.yugioh.gameplay.card.DummyCard;
import org.loujiarong.yugioh.gameplay.effect.Effect;
import org.loujiarong.yugioh.gameplay.effect.ICanActivate;
import org.loujiarong.yugioh.gameplay.effect.IHasEffect;
import org.loujiarong.yugioh.util.Command;
import org.loujiarong.yugioh.util.GameConstants.BattlePhase;
import org.loujiarong.yugioh.util.GameConstants.CardPosition;
import org.loujiarong.yugioh.util.GameConstants.DrawPhase;
import org.loujiarong.yugioh.util.GameConstants.EndPhase;
import org.loujiarong.yugioh.util.GameConstants.Event;
import org.loujiarong.yugioh.util.GameConstants.MainPhase;
import org.loujiarong.yugioh.util.GameConstants.Phase;
import org.loujiarong.yugioh.util.GameConstants.StandbyPhase;



public abstract class Duel
{
	
   protected Player playerA, playerB, turnPlayer, priorityPlayer, winner;
   protected Phase phase;
   protected DrawPhase dp;
   protected StandbyPhase sp;
   protected MainPhase m1, m2;
   protected BattlePhase bp;
   protected EndPhase ep;
   protected Hashtable<Card, Effect> 
	   continuousEffectTab,
	   turnPlayerCompulsoryTriggerEffectTab, 
	   turnPlayerOptTriggerEffectTab,
       oppPlayerCompulsoryTriggerEffectTab,
       oppPlayerOptTriggerEffectTab,
       turnPlayerIgnitionEffectTab, 
       oppPlayerIgnitionEffectTab;
   
   
   protected Stack<Effect> chain;
   protected int turn;
   
   public Duel() {
	   winner = null;
	   turn = 0;

   }
   	
   public boolean checkCondition() {
	   continuousEffectTab = new Hashtable<Card, Effect>();
	   turnPlayerCompulsoryTriggerEffectTab = new Hashtable<Card, Effect>();
	   turnPlayerOptTriggerEffectTab = new Hashtable<Card, Effect>();
	   oppPlayerCompulsoryTriggerEffectTab = new Hashtable<Card, Effect>();
	   oppPlayerOptTriggerEffectTab = new Hashtable<Card, Effect>();    
	   return checkCondition(playerA) || checkCondition(playerB);
   }
   
	public boolean checkCondition(Player player) {
		List<Card> hand = player.getHand().getCards();
		List<Card> graveYard = player.getGraveYard().getCards();
		List<Card> banish =  player.getRemovedFromPlayArea().getCards();
		List<Card> field = new ArrayList<Card>();
		for(Card card : player.getField().getMonsterZone()) {
			if(card != DummyCard.getDummyCard()) {
				field.add(card);
			}
		}
		for(Card card : player.getField().getSpellTrapZone()) {
			if(card != DummyCard.getDummyCard() && 
					card.getPos() != CardPosition.FACE_DOWN &&
					card.getPos() != CardPosition.FACE_DOWN_ATTACK) {
				field.add(card);
			}
		}
		return checkCondition(hand) 
		|| checkCondition(graveYard)
		|| checkCondition(banish)
		|| checkCondition(field);
	}
	
	public boolean checkCondition(List<Card> cards) {
		boolean meetCondition = false;
		for(Card card : cards) {
			if(card.checkCondition(this)) {
				meetCondition = true;
			}
		}
		return meetCondition;
	}

	
	public void checkActivationStack(Event event) {
		for(Card card : activationStack.get(event)) {
			if(card.getController() == priorityPlayer) {
				System.out.println(priorityPlayer + ", do you want to call priority?[y/n]");
				Scanner input = new Scanner(System.in);
				boolean nextStep = false;
				String cmd;
				while(input.hasNext()) {
					cmd = input.next();
					if(cmd.equals(Command.NO)) {
						switchPriority();
						System.out.println(priorityPlayer + ", do you want to call priority?");
						while(input.hasNext()) {
							 cmd = input.next();
							 if(cmd.equals(Command.NO)) {
								 System.out.println("both player give up priority");
								 nextStep = true;
								 break;
							 }
							 else if(cmd.equals(Command.YES)) {
								 System.out.println(priorityPlayer + "call priority");
							 }
						}
						if(nextStep)
							break;
				   }
				   else if(cmd.equals(Command.YES)) {
					   while(input.hasNext()) {
						   System.out.println(priorityPlayer + ", select the card you want to activate");
						   cmd = input.next();
						   if(cmd.equals(Command.CANCEL))
							   break;
						   Card selectedCard = Command.parseSelectCardCmd(cmd, priorityPlayer);
						   if(selectedCard == null || !(selectedCard instanceof ICanActivate)) {
							   System.out.println(priorityPlayer + ", the cards you select can not be used or activated");
							   continue;
						   }
						   else {
							   ((ICanActivate)selectedCard).activate(this);
						   }
					   }
				   }

				}  
			}
		}
	}
	
	public void switchPriority() {
		if(priorityPlayer == playerA) {
			priorityPlayer = playerB;
		}
		else if(priorityPlayer == playerB) {
			priorityPlayer = playerA;
		}
		else {
			System.err.println("priority switch error");
		}
	}
	
	public void switchTurnPlayer() {
		if(turnPlayer == playerA) {
			turnPlayer = playerB;
		}
		else if(turnPlayer == playerB) {
			turnPlayer = playerA;
		}
		else {
			System.err.println("turn player switch error");
		}
	}
	
	public Player getOppPlayer() {
		if(turnPlayer == playerA) {
			return playerB;
		}
		else if(turnPlayer == playerB) {
			return playerA;
		}
		else {
			System.err.println("turn player switch error");
			return null;
		}
	}
	
	public void play() {
		if(phase == Phase.DRAW)
			playDrawPhase();
		if(phase == Phase.STANDBY)
			playStandbyPhase();
		if(phase == Phase.STANDBY)
			playStandbyPhase();
		if(phase == Phase.STANDBY)
			playStandbyPhase();
		if(phase == Phase.STANDBY)
			playStandbyPhase();
	}
	
	public void playDrawPhase() {
		if(dp == DrawPhase.DECLARATION) {
			checkContinuousTab();
			checkTriggerEffectTab(turnPlayerCompulsoryTriggerEffectTab, turnPlayer, false);
			checkTriggerEffectTab(oppPlayerCompulsoryTriggerEffectTab, getOppPlayer(), false);
			checkTriggerEffectTab(turnPlayerOptTriggerEffectTab, turnPlayer, true);
			checkTriggerEffectTab(oppPlayerOptTriggerEffectTab, getOppPlayer(), true);
			checkCondition();
			checkIgnitionEffectTab();
			
			while(!chain.isEmpty()) {
				Effect e = chain.pop();
				e.resolve(this);
			}
		}
		
	}
	
	public void checkIgnitionEffectTab() {
		if(turnPlayerIgnitionEffectTab != null && turnPlayerIgnitionEffectTab.size() > 1) {
			System.out.println(turnPlayer + ", plz arrange the chain of the following cards [pos pos] ");
			Scanner input = new Scanner(System.in);
			while(input.hasNext()) {
				if(parseSelfChainCmd(input.next(),tab, player, optional))
					break;
				else
					System.out.print("[cmd] ");
			}
		}
	}
	

	public void checkContinuousTab() {
		if(continuousEffectTab != null) {
			Iterator<Card> itr = continuousEffectTab.keySet().iterator();
			while(itr.hasNext()) {
				Card card = itr.next();
				System.out.println(card.getFieldPos() + " " + card.getName());
				continuousEffectTab.get(card).resolve(this);
			}
		}
		else {
			System.err.println("continuous effect tab not initialized");
		}
	}
	
	public void checkTriggerEffectTab(Hashtable<Card, Effect> tab, Player player, boolean optional) {
		if(tab != null) {
			if(tab.size() > 1) {
				System.out.println(player + ", plz arrange the chain of the following cards [pos pos] ");
				Iterator<Card> itr = tab.keySet().iterator();
				while(itr.hasNext()) {
					Card card = itr.next();
					System.out.println(card.getFieldPos() + " " + card.getName());
				}
				Scanner input = new Scanner(System.in);
				while(input.hasNext()) {
					int pos = Integer.parseInt(input.next());
					if(parseIgnitionCmd(input.next(),tab, player))
						break;
					else
						System.out.print("[cmd] ");
				}
				printChain();
			}
			else {
				Iterator<Card> itr = tab.keySet().iterator();
				while(itr.hasNext()) {
					Card card = itr.next();
					System.out.println(card.getFieldPos() + " " + card.getName());
					System.out.println("push card " + card + "into chain");
					chain.push(tab.get(card));					
				}
				printChain();
			}				
		}
		else {
			System.err.println("trigger effect tab not initialized");
		}
	}
	
	
	public boolean parseIgnitionCmd(String cmd, Hashtable<Card, Effect> tab, Player player) {
		int pos = Integer.parseInt(cmd);
		if(pos>=1 && pos <=5){
			Card c = player.getField().getSpellTrapZone()[pos-1];
			if(tab.containsKey(c)){
				chain.push(tab.get(c));
			} 
			else {
				chain.clear();
				return false;
			}
		}
		else if(pos>=6 && pos <=10) {
			Card c = player.getField().getMonsterZone()[pos-6];
			if(tab.containsKey(c)){
				chain.push(tab.get(c));
			} 
			else {
				chain.clear();
				return false;
			}
		}
		return true;
	}
	
	public void printChain() {
		System.out.println("Cards in the chain: ");
		for(Effect e : chain) {			
			System.out.print(e.getCard() + " ");
		}
	}
	
	public boolean parseSelfChainCmd(String cmd, Hashtable<Card, Effect> tab, Player player, boolean optional) {
		int i = 0;
		Queue<Effect> tmp = new LinkedList<Effect>();
		for(String p : cmd.split(" ")) {
			int pos = Integer.parseInt(p);
			if(pos>=1 && pos <=5){
				Card c = player.getField().getSpellTrapZone()[pos-1];
				if(tab.containsKey(c)){
					tmp.offer(tab.get(c));
				} 
				else {
					return false;
				}
			}
			else if(pos>=6 && pos <=10) {
				Card c = player.getField().getMonsterZone()[pos-6];
				if(tab.containsKey(c)){
					tmp.offer(tab.get(c));
				} 
				else {
					return false;
				}
			}
			++i;
		}
		if( (tab.size() == i && !optional) 
				|| (tab.size() > i && optional)) {
			while (!tmp.isEmpty()) {
				chain.push(tmp.poll());
			}
			return true;
		}
		else
			return false;
	}
	
	
	public void playStandbyPhase() {

	}
	
	public Duel duel(){				
		while(true) {
			if(winner == null)
				drawPhase();
			else
				return this;
			if(winner == null)
				standbyPhase();
			else
				return this;
			if(winner == null)
				mainPhase1();
			else
				return this;
			if(winner == null)
				battlePhase();
			else
				return this;
			if(winner == null)
				mainPhase2();
			else
				return this;
			if(winner == null)
				endPhase();
			else
				return this;				
			switchTurnPlayer();
		}
	}

	private void drawPhase() {
		phase = Phase.DRAW;
		System.out.println(turnPlayer + "@" + phase);	
		priorityPlayer = turnPlayer;
		dp = DrawPhase.DECLARATION;
		System.out.println(turnPlayer + "@DrawPhase.DECLARATION");
		if(checkCondition())
			play();
		dp = DrawPhase.PREDRAW_EFFECT;
		if(checkCondition())
			play();
		dp = DrawPhase.PREDRAW_CHAIN1;
		if(checkCondition())
			play();
		dp = DrawPhase.PREDRAW_CHAIN2;
		if(checkCondition())
			play();
		dp = DrawPhase.DRAW_EFFECT;
		if(checkCondition())
			play();
		dp = DrawPhase.DRAW_TRIGGER;
		if(checkCondition())
			play();
		dp = DrawPhase.DRAW_SPEED2;
		if(checkCondition())
			play();
		dp = DrawPhase.POSTDRAW_SPEED2;
		if(checkCondition())
			play();
		
	}
	
	private void standbyPhase() {
		// TODO Auto-generated method stub
		
	}
	
	private void mainPhase1() {
		// TODO Auto-generated method stub
		
	}

	private void battlePhase() {
		// TODO Auto-generated method stub
		
	}
	
	private void mainPhase2() {
		// TODO Auto-generated method stub
		
	}
	
	private void endPhase() {
		// TODO Auto-generated method stub
		
	}
   
   public Player getPlayerA() {
		return playerA;
   }
   public void setPlayerA(Player playerA) {
		this.playerA = playerA;
   }
   public Player getPlayerB() {
		return playerB;
   }
   public void setPlayerB(Player playerB) {
	    this.playerB = playerB;
   }
   
	public Player getTurnPlayer() {
		return turnPlayer;
	}
	public void setTurnPlayer(Player turnPlayer) {
		this.turnPlayer = turnPlayer;
	}
	public Phase getPhase() {
		return phase;
	}
	public void setPhase(Phase phase) {
		this.phase = phase;
	}

	public Player getWinner() {
		return winner;
	}

	public void setWinner(Player winner) {
		this.winner = winner;
	}

}
